package dto

import (
	"engine/consts"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"lib/db"
	"lib/log"
	"runtime/debug"
	"strings"
)

func Create[T any](v T) error {
	return db.GetDb().Create(v).Error
}

func CreateOmit[T any](v T, omit ...string) error {
	return db.GetDb().Omit(omit...).Create(v).Error
}

func Save[T any](v T) error {
	return db.GetDb().Omit("id", "created_at", "updated_at").Save(v).Error
}

func Update[T any](v T) error {
	return db.GetDb().Model(v).Updates(v).Error
}

func Delete[T any](v T) error {
	return db.GetDb().Delete(v).Error
}

func QueryOne[T any](query string, args ...any) (*T, error) {
	var t T
	err := db.GetDb().Where(query, args...).First(&t).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return &t, nil
}

func QueryTableOne[T any](table, query string, args ...any) (*T, error) {
	var t T
	err := db.GetDb().Table(table).Where(query, args...).First(&t).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return &t, nil
}

func QueryOneById[T any](id any) (*T, error) {
	var t T
	err := db.GetDb().Where("id = ?", id).First(&t).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return &t, nil
}

func QueryList[T any](query string, args ...any) ([]*T, error) {
	var t []*T
	err := db.GetDb().Where(query, args...).Find(&t).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return t, nil
}

func QueryOneColumn[T any, M any](column string, query string, args ...any) ([]T, error) {
	var t []T
	err := db.GetDb().Model(new(M)).Where(query, args...).Pluck(column, &t).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return t, nil
}

func QueryTableList[T any](table, query string, args ...any) ([]*T, error) {
	var t []*T
	err := db.GetDb().Table(table).Where(query, args...).Find(&t).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return t, nil
}

func QueryTablePageList[T any](page, pageSize int, query string, args ...any) (*PaginationList[T], error) {
	paginationList := &PaginatorOption{
		Page:     page,
		PageSize: pageSize,
		//ShowSQL:  true, //打印sql
	}

	if strings.EqualFold(query, "") {
		return Paginate[T](db.GetDb(), paginationList)
	}

	return Paginate[T](db.GetDb().Where(query, args...), paginationList)
}

func QueryAll[T any]() ([]*T, error) {
	var t []*T
	err := db.GetDb().Find(&t).Error

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return t, nil
}

func QueryCount[T any](query string, args ...any) (int64, error) {
	count := int64(0)
	var t T
	err := db.GetDb().Model(t).Where(query, args...).Count(&count).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, nil
		}
		return 0, err
	}
	return count, nil
}

type PaginationList[T any] struct {
	Items    []*T    `json:"items"`
	PageSize int     `json:"pageSize"`
	Page     int     `json:"page"`
	Total    int64   `json:"total"`
	Mmm      float64 `json:"mmm"`
}

type PaginatorOption struct {
	Page     int `json:"page"`
	PageSize int `json:"pageSize"`
	ShowSQL  bool
}

func Paginate[T any](DB *gorm.DB, options *PaginatorOption) (*PaginationList[T], error) {
	var (
		done        = make(chan bool, 1)
		dbSession   = DB.Session(&gorm.Session{})
		defaultPage = 1
		offset      int
	)

	// 如果没有传分页参数，需要默认的
	if options == nil {
		options = &PaginatorOption{}
	}

	paginator := PaginationList[T]{
		Page:     defaultPage,
		PageSize: consts.DefaultPageSize,
		Items:    make([]*T, 0, options.PageSize),
	}

	// 异步请求总数
	go getCounts(dbSession, paginator.Items, done, &paginator.Total)

	// 是否输出 SQL 语句方便调试
	if options.ShowSQL {
		dbSession = dbSession.Debug()
	}

	if options.PageSize > 0 {
		paginator.PageSize = options.PageSize
	}

	if options.Page > 0 {
		paginator.Page = options.Page
	}

	if paginator.Page > 1 {
		offset = (paginator.Page - 1) * paginator.PageSize
	}

	// 获取数据
	errGet := dbSession.Limit(paginator.PageSize).Offset(offset).Find(&paginator.Items).Error
	<-done
	if errGet != nil { // && !errors.Is(errGet, gorm.ErrRecordNotFound)
		if errors.Is(errGet, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errGet
	}

	return &paginator, nil
}

func getCounts(db *gorm.DB, anyType interface{}, done chan bool, count *int64) {
	defer func() {
		if e := recover(); e != nil { // 捕获该协程的panic
			debugInfo := debug.Stack()
			log.Error(fmt.Sprintf("getCounts Err: %v\n%s", e, string(debugInfo)))
		}
	}()
	db.Model(anyType).Count(count)
	done <- true
}
